﻿// 2020/6/18: 首个版本
// 2021/5/13: 修正轮廓解析问题
// 2021/7/7: 增加vehicleSampleProtocols
// 2023/10/12: 补充部分英文注释
// 2024/3/19: 支持vehicle-sample-v5
// 2024/3/24: 支持object-vehicle-sample-v1@x通道
// 2024/11/5: 修正getVehicleSample
// 2025/4/19: 新增大灯位置坐标和相对于地面俯仰角字段
// 2025/4/30: 增加vehicleSampleConverters

var GearPosition = {
    Unknown : 0,  // 未知
    Park : 1,  // P档
    Reverse : 2,  // R档
    Neutral : 3,  // N档
    Drive : 4,  // D档
};

var TurnState = {
    Unknown : 0,  // 未知
    None : 1,  // 未打灯
    TurnLeft : 2,  // 左转向灯
    TurnRight : 3,  // 右转向灯
    Emergency : 4,  // 双闪
};

var Horn = {
    Unknown : 0,  // 未知
    None : 1,  // 不响
    Active : 2,  // 响
};

var HeadLight = {
    Unknown : 0,  // 未知
    None : 1,  // 未激活
    Near : 2,  // 近光
    Far : 3,  // 远光
};

var Wiper = {
    Unknown : 0,  // 未知
    None : 1,  // 未激活
    Single : 2,  // 单次
    Slow1 : 3,  // 慢 1
    Slow2 : 4,  // 慢 2
    Slow3 : 5,  // 慢 3
    Slow4 : 6,  // 慢 4
    Slow5 : 7,  // 慢 5
    Medium : 8,  // 中档
    Fast : 9,  // 快档
};

function VehicleSample(session, time) {
    this.session = session; // Date or AppoxSessionIdentifier
    this.time = time;  // [s] Time offset in session / Session内的相对时间
    this.speed = 0.0;  // [kph] 本车车速
    this.yawRate = null;  // [deg/s] 横摆角速度
    this.curvature = null;  // [1/m] Turning left is positive, while the driving path is / 行驶曲率，左转为正，未来路径: y=(curvature/2)*(x^2)+(curvature)*(frontOverhang+wheelBase)*x
    this.kilometerAge = 0.0;  // [km] 总里程数
    this.accelX = null;  // [m/s2] 本车纵向加速度
    this.accelY = null;  // [m/s2] 本车横向加速度
    this.jerkX = null;  // [m/s3] 本车纵向急动度
    this.jerkY = null;  // [m/s3] 本车横向急动度
    this.vehicleWidth = 1.9;  // [m] Constant / 车宽（常量）
    this.vehicleLength = 4.6;  // [m] Constant / 车长（常量）
    this.vehicleHeight = 1.5;  // [m] Constant / 车高（常量）
    this.steerAngleRatio = 15;  // Constant / 传动比（常量）
    this.wheelBase = 2.8;  // [m] Constant / 轴距（常量）
    this.rearTread = 1.6;  // [m] Constant / 轮距（常量）
    this.frontOverhang = 0.9;  // [m] Constant / 前悬（常量）
    this.maxCurvature = null;  // [1/m] Max absolute curvature value (Constant) / 最大行驶曲率，不分左右（常量）
    this.maxAcceleration = null;  // [m/s2] Max absolute longitudinal acceleration value for forward driving (Constant) / 前向行驶最大加速度（常量）
    this.maxDeceleration = null;  // [m/s2] Max absolute longitudinal deceleration value for forward driving (Constant) / 前向行驶最大减速度（常量）
    this.maxAccelY = null;  // [m/s2] Allowed max absolute lateral acceleration value (Constant) / 最大允许横向加速度，不分左右（常量）
    this.maxSteerAngle = null;  // [deg] Constant / 最大方向盘转角（常量）
    this.maxSteerAngleRate = null;  // [deg/s] Constant / 最大方向盘转角速度（常量）
    this.brakeDeadZone = null;  // [%] Constant / 刹车踏板死区（常量）
    this.throttleDeadZone = null;  // [%] Constant / 油门踏板死区（常量）
    this.flMass = null;  // [kg] Mass on front left wheel (Constant) / 左前轮上的质量（常量）
    this.frMass = null;  // [kg] Mass on front right wheel (Constant) / 右前轮上的质量（常量）
    this.rlMass = null;  // [kg] Mass on rear left wheel (Constant) / 左后轮上的质量（常量）
    this.rrMass = null;  // [kg] Mass on rear right wheel (Constant) / 右后轮上的质量（常量）
    this.frontCorneringStiffness = null;  // [N/rad] Sum of front left and front right wheel's cornering stiffness (Constant) / 前轮侧偏刚度，左右轮之和（常量）
    this.rearCorneringStiffness = null;  // [N/rad] Sum of rear left and rear right wheel's cornering stiffness (Constant) / 后轮侧偏刚度，左右轮之和（常量）
    this.steerAngle = null;  // [deg] 方向盘转角
    this.steerAngleRate = null;  // [deg/s] 方向盘转角速度
    this.steerTorque = null;  // [Nm] 方向盘力矩
    this.brake = null;  // [%] 刹车位置
    this.throttle = null;  // [%] 油门位置
    this.gear = GearPosition.Unknown;  // 档位
    this.turn = TurnState.Unknown;  // 转向灯
    this.horn = Horn.Unknown;  // 喇叭
    this.headLight = HeadLight.Unknown;  // 大灯
    this.wiper = Wiper.Unknown;  // 雨刮
    this.flSpeed = null;  // [kph] Speed of front left wheel / 左前轮轮速
    this.frSpeed = null;  // [kph] Speed of front right wheel / 右前轮轮速
    this.rlSpeed = null;  // [kph] Speed of rear left wheel / 左后轮轮速
    this.rrSpeed = null;  // [kph] Speed of rear right wheel / 右后轮轮速
    this.flAngle = null;  // [deg] Direction of front left wheel / 左前轮朝向角
    this.frAngle = null;  // [deg] Direction of front right wheel / 右前轮朝向角
    this.engineSpeed = null;  // [rpm] 引擎转速
    this.engineTorque = null;  // [Nm] 引擎扭矩
    this.frontEndLateralSpeed = null;  // [m/s] 前保中心横向速度
    this.rearEndLateralSpeed = null;  // [m/s] 后保中心横向速度
    this.trajectory = [];  // 历史路径
    this.contour = [];  // The first point and the last point are both (0,0), forming a closed curve (Constant) / 轮廓描述，以(0,0)开始，以(0,0)结束（常量）
    this.flLightPosX = null;  // [m] X Coordinate of front left light (the same as front right light) / 左前灯X坐标(即右前灯X坐标)
    this.flLightPosY = null;  // [m] Y Coordinate of front left light (negative value of front right light) / 左前灯Y坐标(右前灯Y坐标为其负值)
    this.flLightPosZ = null;  // [m] Z Coordinate of front left light (the same as front right light) / 左前灯Z坐标(即右前灯Z坐标)
    this.rlLightPosX = null;  // [m] X Coordinate of rear left light (the same as rear right light) / 左后灯X坐标(即右后灯X坐标)
    this.rlLightPosY = null;  // [m] Y Coordinate of rear left light (negative value of rear right light) / 左后灯Y坐标(右后灯Y坐标为其负值)
    this.rlLightPosZ = null;  // [m] Z Coordinate of rear left light (the same as rear right light) / 左后灯Z坐标(即右后灯Z坐标)
    this.pitchToGround = null;  // [deg] Vehicle's pitch angle to ground / 车辆相对于地面的俯仰角
}

function convVehicleSampleV3(gs) {
    if (gs.values.length < 27) return null;

    var trajectorySize = gs.values[0] == null ? 0 : gs.values[0];
    if (gs.values.length != 27 && gs.values.length != 27 + 2 * trajectorySize) return null;

    var sample = new VehicleSample(gs.session, gs.time);
    if (gs.values[1] != null) sample.speed = gs.values[1];
    sample.yawRate = gs.values[2];
    sample.curvature = gs.values[3];
    sample.accelX = gs.values[4];
    sample.accelY = gs.values[5];
    if (gs.values[6] != null) sample.vehicleWidth = gs.values[6];
    if (gs.values[7] != null) sample.vehicleLength = gs.values[7];
    if (gs.values[8] != null) sample.steerAngleRatio = gs.values[8];
    if (gs.values[9] != null) sample.wheelBase = gs.values[9];
    if (gs.values[10] != null) sample.rearTread = gs.values[10];
    if (gs.values[11] != null) sample.frontOverhang = gs.values[11];
    sample.steerAngle = gs.values[12];
    sample.brake = gs.values[13];
    sample.throttle = gs.values[14];
    if (gs.values[15] != null) sample.gear = gs.values[15];
    if (gs.values[16] != null) sample.turn = gs.values[16];
    sample.flSpeed = gs.values[17];
    sample.frSpeed = gs.values[18];
    sample.rlSpeed = gs.values[19];
    sample.rrSpeed = gs.values[20];
    sample.engineSpeed = gs.values[21];
    sample.engineTorque = gs.values[22];
    if (gs.values[23] != null) sample.horn = gs.values[23];
    if (gs.values[24] != null) sample.headLight = gs.values[24];
    if (gs.values[25] != null) sample.wiper = gs.values[25];
    if (gs.values[26] != null) sample.kilometerAge = gs.values[26];

    if (trajectorySize > 0)
    {
        sample.trajectory = new Array(trajectorySize);
        for (var i = 0; i < trajectorySize; i++) {
            sample.trajectory[i] = new BIPoint(gs.values[27 + 2 * i], gs.values[28 + 2 * i]);
        }
    }

    return sample;
}

function convVehicleSampleV4(gs) {
    if (gs.values.length < 40) return null;

    var contourSize = gs.values[0] == null ? 0 : gs.values[0];
    var trajectorySize = gs.values[1] == null ? 0 : gs.values[1];
    var sizeWithoutTraj = 40 + 2 * contourSize;
    var sizeWithTraj = 40 + 2 * (contourSize + trajectorySize);
    if (gs.values.length != sizeWithoutTraj && gs.values.length != sizeWithTraj) return null;

    var sample = new VehicleSample(gs.session, gs.time);
    if (gs.values[2] != null) sample.speed = gs.values[2];
    sample.yawRate = gs.values[3];
    sample.curvature = gs.values[4];
    sample.accelX = gs.values[5];
    sample.accelY = gs.values[6];
    if (gs.values[7] != null) sample.vehicleWidth = gs.values[7];
    if (gs.values[8] != null) sample.vehicleLength = gs.values[8];
    if (gs.values[9] != null) sample.vehicleHeight = gs.values[9];
    if (gs.values[10] != null) sample.steerAngleRatio = gs.values[10];
    if (gs.values[11] != null) sample.wheelBase = gs.values[11];
    if (gs.values[12] != null) sample.rearTread = gs.values[12];
    if (gs.values[13] != null) sample.frontOverhang = gs.values[13];
    sample.steerAngle = gs.values[14];
    sample.steerAngleRate = gs.values[15];
    sample.brake = gs.values[16];
    sample.throttle = gs.values[17];
    if (gs.values[18] != null) sample.gear = gs.values[18];
    if (gs.values[19] != null) sample.turn = gs.values[19];
    sample.flSpeed = gs.values[20];
    sample.frSpeed = gs.values[21];
    sample.rlSpeed = gs.values[22];
    sample.rrSpeed = gs.values[23];
    sample.engineSpeed = gs.values[24];
    sample.engineTorque = gs.values[25];
    if (gs.values[26] != null) sample.horn = gs.values[26];
    if (gs.values[27] != null) sample.headLight = gs.values[27];
    if (gs.values[28] != null) sample.wiper = gs.values[28];
    if (gs.values[29] != null) sample.kilometerAge = gs.values[29];
    sample.frontEndLateralSpeed = gs.values[30];
    sample.rearEndLateralSpeed = gs.values[31];
    sample.steerTorque = gs.values[32];
    sample.jerkX = gs.values[33];
    sample.jerkY = gs.values[34];

    if (contourSize > 0) {
        sample.contour = new Array(contourSize);
        for (var i = 0; i < contourSize; i++) {
            sample.contour[i] = new BIPoint(gs.values[40 + 2 * i], gs.values[41 + 2 * i]);
        }
    }
    if (gs.values.length == sizeWithTraj && trajectorySize > 0) {
        sample.trajectory = new Array(trajectorySize);
        for (var i = 0; i < trajectorySize; i++) {
            sample.trajectory[i] = new BIPoint(gs.values[sizeWithoutTraj + 2 * i], gs.values[sizeWithoutTraj + 2 * i + 1]);
        }
    }

    return sample;
}

function convVehicleSampleV5(gs) {
    if (gs.values.length < 80) return null;

    var contourSize = gs.values[0] == null ? 0 : gs.values[0];
    var trajectorySize = gs.values[1] == null ? 0 : gs.values[1];
    var sizeWithoutTraj = 80 + 2 * contourSize;
    var sizeWithTraj = 80 + 2 * (contourSize + trajectorySize);
    if (gs.values.length != sizeWithoutTraj && gs.values.length != sizeWithTraj) return null;

    var sample = new VehicleSample(gs.session, gs.time);
    if (gs.values[2] != null) sample.speed = gs.values[2];
    sample.yawRate = gs.values[3];
    sample.curvature = gs.values[4];
    sample.accelX = gs.values[5];
    sample.accelY = gs.values[6];
    if (gs.values[7] != null) sample.vehicleWidth = gs.values[7];
    if (gs.values[8] != null) sample.vehicleLength = gs.values[8];
    if (gs.values[9] != null) sample.vehicleHeight = gs.values[9];
    if (gs.values[10] != null) sample.steerAngleRatio = gs.values[10];
    if (gs.values[11] != null) sample.wheelBase = gs.values[11];
    if (gs.values[12] != null) sample.rearTread = gs.values[12];
    if (gs.values[13] != null) sample.frontOverhang = gs.values[13];
    sample.steerAngle = gs.values[14];
    sample.steerAngleRate = gs.values[15];
    sample.brake = gs.values[16];
    sample.throttle = gs.values[17];
    if (gs.values[18] != null) sample.gear = gs.values[18];
    if (gs.values[19] != null) sample.turn = gs.values[19];
    sample.flSpeed = gs.values[20];
    sample.frSpeed = gs.values[21];
    sample.rlSpeed = gs.values[22];
    sample.rrSpeed = gs.values[23];
    sample.engineSpeed = gs.values[24];
    sample.engineTorque = gs.values[25];
    if (gs.values[26] != null) sample.horn = gs.values[26];
    if (gs.values[27] != null) sample.headLight = gs.values[27];
    if (gs.values[28] != null) sample.wiper = gs.values[28];
    if (gs.values[29] != null) sample.kilometerAge = gs.values[29];
    sample.frontEndLateralSpeed = gs.values[30];
    sample.rearEndLateralSpeed = gs.values[31];
    sample.steerTorque = gs.values[32];
    sample.jerkX = gs.values[33];
    sample.jerkY = gs.values[34];
    sample.maxCurvature = gs.values[35];
    sample.maxAcceleration = gs.values[36];
    sample.maxDeceleration = gs.values[37];
    sample.maxAccelY = gs.values[38];
    sample.maxSteerAngle = gs.values[39];
    sample.maxSteerAngleRate = gs.values[40];
    sample.brakeDeadZone = gs.values[41];
    sample.throttleDeadZone = gs.values[42];
    sample.flMass = gs.values[43];
    sample.frMass = gs.values[44];
    sample.rlMass = gs.values[45];
    sample.rrMass = gs.values[46];
    sample.flAngle = gs.values[47];
    sample.frAngle = gs.values[48];
    sample.frontCorneringStiffness = gs.values[49];
    sample.rearCorneringStiffness = gs.values[50];
    sample.flLightPosX = gs.values[51];
    sample.flLightPosY = gs.values[52];
    sample.flLightPosZ = gs.values[53];
    sample.rlLightPosX = gs.values[54];
    sample.rlLightPosY = gs.values[55];
    sample.rlLightPosZ = gs.values[56];
    sample.pitchToGround = gs.values[57];

    if (contourSize > 0) {
        sample.contour = new Array(contourSize);
        for (var i = 0; i < contourSize; i++) {
            sample.contour[i] = new BIPoint(gs.values[80 + 2 * i], gs.values[81 + 2 * i]);
        }
    }
    if (gs.values.length == sizeWithTraj && trajectorySize > 0) {
        sample.trajectory = new Array(trajectorySize);
        for (var i = 0; i < trajectorySize; i++) {
            sample.trajectory[i] = new BIPoint(gs.values[sizeWithoutTraj + 2 * i], gs.values[sizeWithoutTraj + 2 * i + 1]);
        }
    }

    return sample;
}

// Interpolator / 样本插值函数
function interpolateVehicleSample(session, time, s1, w1, s2, w2) {
    var sample = new VehicleSample(session, time);
    sample.speed = s1.speed * w1 + s2.speed * w2;
    sample.kilometerAge = s1.kilometerAge * w1 + s2.kilometerAge * w2;
    sample.yawRate = s1.yawRate == null || s2.yawRate == null ? null : s1.yawRate * w1 + s2.yawRate * w2;
    sample.curvature = s1.curvature == null || s2.curvature == null ? null : s1.curvature * w1 + s2.curvature * w2;
    sample.accelX = s1.accelX == null || s2.accelX == null ? null : s1.accelX * w1 + s2.accelX * w2;
    sample.accelY = s1.accelY == null || s2.accelY == null ? null : s1.accelY * w1 + s2.accelY * w2;
    sample.jerkX = s1.jerkX == null || s2.jerkX == null ? null : s1.jerkX * w1 + s2.jerkX * w2;
    sample.jerkY = s1.jerkY == null || s2.jerkY == null ? null : s1.jerkY * w1 + s2.jerkY * w2;
    sample.vehicleWidth = s1.vehicleWidth;
    sample.vehicleLength = s1.vehicleLength;
    sample.vehicleHeight = s1.vehicleHeight;
    sample.steerAngleRatio = s1.steerAngleRatio;
    sample.wheelBase = s1.wheelBase;
    sample.rearTread = s1.rearTread;
    sample.frontOverhang = s1.frontOverhang;
    sample.steerAngle = s1.steerAngle == null || s2.steerAngle == null ? null : s1.steerAngle * w1 + s2.steerAngle * w2;
    sample.steerAngleRate = s1.steerAngleRate == null || s2.steerAngleRate == null ? null : s1.steerAngleRate * w1 + s2.steerAngleRate * w2;
    sample.steerTorque = s1.steerTorque == null || s2.steerTorque == null ? null : s1.steerTorque * w1 + s2.steerTorque * w2;
    sample.brake = s1.brake == null || s2.brake == null ? null : s1.brake * w1 + s2.brake * w2;
    sample.throttle = s1.throttle == null || s2.throttle == null ? null : s1.throttle * w1 + s2.throttle * w2;
    sample.gear = w1 > w2 ? s1.gear : s2.gear;
    sample.turn = w1 > w2 ? s1.turn : s2.turn;
    sample.horn = w1 > w2 ? s1.horn : s2.horn;
    sample.headLight = w1 > w2 ? s1.headLight : s2.headLight;
    sample.wiper = w1 > w2 ? s1.wiper : s2.wiper;
    sample.flSpeed = s1.flSpeed == null || s2.flSpeed == null ? null : s1.flSpeed * w1 + s2.flSpeed * w2;
    sample.frSpeed = s1.frSpeed == null || s2.frSpeed == null ? null : s1.frSpeed * w1 + s2.frSpeed * w2;
    sample.rlSpeed = s1.rlSpeed == null || s2.rlSpeed == null ? null : s1.rlSpeed * w1 + s2.rlSpeed * w2;
    sample.rrSpeed = s1.rrSpeed == null || s2.rrSpeed == null ? null : s1.rrSpeed * w1 + s2.rrSpeed * w2;
    sample.engineSpeed = s1.engineSpeed == null || s2.engineSpeed == null ? null : s1.engineSpeed * w1 + s2.engineSpeed * w2;
    sample.engineTorque = s1.engineTorque == null || s2.engineTorque == null ? null : s1.engineTorque * w1 + s2.engineTorque * w2;
    sample.frontEndLateralSpeed = s1.frontEndLateralSpeed == null || s2.frontEndLateralSpeed == null ? null : s1.frontEndLateralSpeed * w1 + s2.frontEndLateralSpeed * w2;
    sample.rearEndLateralSpeed = s1.rearEndLateralSpeed == null || s2.rearEndLateralSpeed == null ? null : s1.rearEndLateralSpeed * w1 + s2.rearEndLateralSpeed * w2;
    sample.maxCurvature = s1.maxCurvature;
    sample.maxAcceleration = s1.maxAcceleration;
    sample.maxDeceleration = s1.maxDeceleration;
    sample.maxAccelY = s1.maxAccelY;
    sample.maxSteerAngle = s1.maxSteerAngle;
    sample.maxSteerAngleRate = s1.maxSteerAngleRate;
    sample.brakeDeadZone = s1.brakeDeadZone;
    sample.throttleDeadZone = s1.throttleDeadZone;
    sample.flMass = s1.flMass;
    sample.frMass = s1.frMass;
    sample.rlMass = s1.rlMass;
    sample.rrMass = s1.rrMass;
    sample.flAngle = s1.flAngle == null || s2.flAngle == null ? null : s1.flAngle * w1 + s2.flAngle * w2;
    sample.frAngle = s1.frAngle == null || s2.frAngle == null ? null : s1.frAngle * w1 + s2.frAngle * w2;
    sample.frontCorneringStiffness = s1.frontCorneringStiffness;
    sample.rearCorneringStiffness = s1.rearCorneringStiffness;
    sample.flLightPosX = w1 > w2 ? s1.flLightPosX : s2.flLightPosX;
    sample.flLightPosY = w1 > w2 ? s1.flLightPosY : s2.flLightPosY;
    sample.flLightPosZ = w1 > w2 ? s1.flLightPosZ : s2.flLightPosZ;
    sample.rlLightPosX = w1 > w2 ? s1.rlLightPosX : s2.rlLightPosX;
    sample.rlLightPosY = w1 > w2 ? s1.rlLightPosY : s2.rlLightPosY;
    sample.rlLightPosZ = w1 > w2 ? s1.rlLightPosZ : s2.rlLightPosZ;
    sample.pitchToGround = s1.pitchToGround * w1 + s2.pitchToGround * w2;
    sample.trajectory = w1 > w2 ? s1.trajectory : s2.trajectory;
    sample.contour = s1.contour;
    return sample;
}

// Supported protocols / 本车数据样本支持解析的协议
var vehicleSampleProtocols = [
    "vehicle-sample-v3",
    "vehicle-sample-v4",
    "vehicle-sample-v5",
    "object-vehicle-sample-v1",
];

// Converters / 通用样本转换函数表
var vehicleSampleConverters = {
    "vehicle-sample-v3": convVehicleSampleV3,
    "vehicle-sample-v4": convVehicleSampleV4,
    "vehicle-sample-v5": convVehicleSampleV5,
    "object-vehicle-sample-v1": convVehicleSampleV5,
};

// Use this function to query subject vehicle sample at target time. If "interpolated" is true, the interpolated frame is preferred, otherwise the nearest frame / 获取指定session指定时刻的主车车辆信息样本，若interpolated为true则优先取插值帧，否则固定为最近帧
function getSubjectVehicleSample(session, time, interpolated) {
    var s1 = null, w1 = null, s2 = null, w2 = null;
    var pairV5 = biGetGeneralSamplePair('vehicle-sample-v5', null, session, time, 1);
    var pairV4 = biGetGeneralSamplePair('vehicle-sample-v4', null, session, time, 1);
    var pairV3 = biGetGeneralSamplePair('vehicle-sample-v3', null, session, time, 1);
    if (pairV5 != null) {
        if (pairV5.sample1 != null) s1 = convVehicleSampleV5(pairV5.sample1);
        if (pairV5.sample2 != null) s2 = convVehicleSampleV5(pairV5.sample2);
        w1 = pairV5.weight1;
        w2 = pairV5.weight2;
    }
    else if (pairV4 != null) {
        if (pairV4.sample1 != null) s1 = convVehicleSampleV4(pairV4.sample1);
        if (pairV4.sample2 != null) s2 = convVehicleSampleV4(pairV4.sample2);
        w1 = pairV4.weight1;
        w2 = pairV4.weight2;
    }
    else if (pairV3 != null) {
        if (pairV3.sample1 != null) s1 = convVehicleSampleV3(pairV3.sample1);
        if (pairV3.sample2 != null) s2 = convVehicleSampleV3(pairV3.sample2);
        w1 = pairV3.weight1;
        w2 = pairV3.weight2;
    }
    else return null;
    if (s1 == null || s2 == null) {
        return s1 == null ? s2 : s1;
    }
    if (interpolated) {
        return interpolateVehicleSample(session, time, s1, w1, s2, w2);
    }
    else {
        return w1 > w2 ? s1 : s2;
    }
}

// Use this function to query object vehicle sample at target time. If "interpolated" is true, the interpolated frame is preferred, otherwise the nearest frame / 获取指定session指定时刻的目标物车辆信息样本，若interpolated为true则优先取插值帧，否则固定为最近帧
function getObjectVehicleSample(channel, session, time, interpolated) {
    var s1 = null, w1 = null, s2 = null, w2 = null;
    var pairV1 = biGetGeneralSamplePair('object-vehicle-sample-v1', channel, session, time, 1);
    if (pairV1 != null) {
        if (pairV1.sample1 != null) s1 = convVehicleSampleV5(pairV1.sample1);
        if (pairV1.sample2 != null) s2 = convVehicleSampleV5(pairV1.sample2);
        w1 = pairV1.weight1;
        w2 = pairV1.weight2;
    }
    else return null;
    if (s1 == null || s2 == null) {
        return s1 == null ? s2 : s1;
    }
    if (interpolated) {
        return interpolateVehicleSample(session, time, s1, w1, s2, w2);
    }
    else {
        return w1 > w2 ? s1 : s2;
    }
}

// Use this function to query subject vehicle sample at target time. If "interpolated" is true, the interpolated frame is preferred, otherwise the nearest frame / 获取指定session指定时刻的主车车辆信息样本，若interpolated为true则优先取插值帧，否则固定为最近帧
function getVehicleSample(session, time, interpolated) {
    return getSubjectVehicleSample(session, time, interpolated);
}